#include <drv_cfg.h>
#include <os_memory.h>
#include <sensors/sensor.h>
#include <math.h>
#include <shell.h>
#include <dlog.h>
#define DBG_TAG "sensor.sensirion.sht30"

/* ADDR Pin Conect to VSS */

// 0x44 左移一位，最后一位是0为读，1为写

uint8_t MEDIUM_2_CMD[2]={0x22,0x20};
uint8_t READOUT_FOR_PERIODIC_MODE[2]={0xE0,0x00};
uint8_t SOFT_RESET_CMD[2]={0x30,0xA2};

typedef struct
{
    struct os_sensor_device sensor;
    struct os_i2c_client    i2c;
    os_uint8_t              id;

    union
    {
        float tempreture;
        float humidity;
    } value;
} sht30_info_t;


static os_err_t write_regs(struct os_i2c_client *client, os_uint16_t reg, os_uint8_t *buf, os_uint8_t len)
{
    struct os_i2c_msg msgs;
    os_uint8_t        databuf[9];
    os_uint8_t        device_addr = 0;
    os_uint8_t        reg_addr    = 0;

    OS_ASSERT(len <= 8);

    if (reg > 255)
    {
        device_addr = OS_SHT30_I2C_ADDR | 0x01;
    }
    else
    {
        device_addr = OS_SHT30_I2C_ADDR;
    }
    reg_addr   = reg & 0xFF;
    databuf[0] = reg_addr;

    memcpy(&databuf[1], buf, len);

    msgs.addr  = device_addr;
    msgs.flags = OS_I2C_WR;
    msgs.buf   = databuf;
    msgs.len   = len + 1;

    if (os_i2c_transfer(client->bus, &msgs, 1) == 1)
    {
        return OS_EOK;
    }
    else
    {
        os_kprintf("Writing command error\n");
        return -OS_ERROR;
    }
}

static os_err_t read_regs(struct os_i2c_client *client, os_uint16_t reg, os_uint8_t *buf, os_uint8_t len)
{
    struct os_i2c_msg msgs[2];
    os_uint8_t        device_addr = 0;
    os_uint8_t        reg_addr    = 0;

    if (reg > 255)
    {
        device_addr = OS_SHT30_I2C_ADDR | 0x01;
    }
    else
    {
        device_addr = OS_SHT30_I2C_ADDR;
    }
    reg_addr = reg & 0xFF;

    msgs[0].addr  = device_addr;
    msgs[0].flags = OS_I2C_WR;
    msgs[0].buf   = &reg_addr;
    msgs[0].len   = 1;

    msgs[1].addr  = device_addr;
    msgs[1].flags = OS_I2C_RD;
    msgs[1].buf   = buf;
    msgs[1].len   = len;

    if (os_i2c_transfer(client->bus, msgs, 2) == 2)
    {
        return OS_EOK;
    }
    else
    {
        os_kprintf("Reading command error\n");
        return -OS_ERROR;
    }
}

static void sht30_reset(sht30_info_t *sht30)
{
    //write_regs(&sht30->i2c, 0, SOFT_RESET_CMD,sizeof(SOFT_RESET_CMD));
    os_i2c_client_write(&sht30->i2c,0,0,SOFT_RESET_CMD,sizeof(SOFT_RESET_CMD));
}

/*  SHT30 初始化，开启连续读取模式*/
static void sht30_init_all(sht30_info_t *sht30)
{
    //write_regs(&sht30->i2c, 0, MEDIUM_2_CMD,sizeof(MEDIUM_2_CMD));
    os_i2c_client_write(&sht30->i2c,0,0,MEDIUM_2_CMD,sizeof(MEDIUM_2_CMD));
}




#define CRC8_POLYNOMIAL 0x31
uint8_t SHT3x_CheckCrc(uint8_t* const message, uint8_t initial_value)
{
    uint8_t  remainder;	    //余数
    uint8_t  i = 0, j = 0;  //循环变量

    /* 初始化 */
    remainder = initial_value;

    for(j = 0; j < 2;j++)
    {
        remainder ^= message[j];

        /* 从最高位开始依次计算  */
        for (i = 0; i < 8; i++)
        {
            if (remainder & 0x80)
            {
                remainder = (remainder << 1)^CRC8_POLYNOMIAL;
            }
            else
            {
                remainder = (remainder << 1);
            }
        }
    }

    /* 返回计算的CRC码 */
    return remainder;
}

static float SHT3x_CalcTemperatureC(unsigned short u16sT)
{
    float temperatureC = 0;

    /* T= -46.85 + 175.72 * ST/2^16 */
    temperatureC = -45 + 175*((float)u16sT/65535);

    os_kprintf("tempature=%d \n",(int)temperatureC);
    return temperatureC;
}

static float SHT3x_CalcRH(unsigned short u16sRH)
{
    float humidityRH = 0;

    /* HumidityRH = -6.0 + 125.0/65536 * (float)u16sRH */
    humidityRH = 100 * ((float)u16sRH / 65535);

    os_kprintf("humidityRH= %d \n",(int)humidityRH);
    return humidityRH;
}

static unsigned short SHT3x_MeasureHM(sht30_info_t *sht30, unsigned char cmd )
{

    uint8_t buff[6];
	buff[0] = 0;
    
    /*  发送周期读命令函数  */
    //write_regs(&sht30->i2c, 0, READOUT_FOR_PERIODIC_MODE,sizeof(READOUT_FOR_PERIODIC_MODE));
    os_i2c_client_write(&sht30->i2c,0,0,READOUT_FOR_PERIODIC_MODE,sizeof(READOUT_FOR_PERIODIC_MODE));
    os_task_msleep(80);
        
	
    //这里连续读6个字节
    //read_regs(&sht30->i2c,0,buff,sizeof(buff));
    os_i2c_client_read(&sht30->i2c,0,0,buff,sizeof(buff));
	//os_i2c_client_read(&sht30->i2c, OS_SHT30_I2C_ADDR, 1, buff, 1);
	
    /*buff[0] = sht30_read_user_reg(sht30);
    buff[1] = sht30_read_user_reg(sht30);
    buff[2] = sht30_read_user_reg(sht30);
    buff[3] = sht30_read_user_reg(sht30);
    buff[4] = sht30_read_user_reg(sht30);
    buff[5] = sht30_read_user_reg(sht30);*/

    os_kprintf("buff=    %d   %d\n",buff[0],buff[3]);

    if(SHT3x_CheckCrc(buff, 0xFF) != buff[2] || SHT3x_CheckCrc(&buff[3], 0xFF) != buff[5])
    {
        os_kprintf("crc is error\n");
        return 1;
    }

    else
    {
        if(cmd == 0x01)
        {
            return ((buff[0] << 8) | buff[1]);
        }
        if(cmd == 0x02)
        {
            return ((buff[3] << 8) | buff[4]);
        }
    }
    return (unsigned short)-1;
}

static void sht30_get_temp(sht30_info_t *sht30)
{
    unsigned short tmp;

    tmp                     = SHT3x_MeasureHM(sht30, 0x01);
    sht30->value.tempreture = SHT3x_CalcTemperatureC(tmp);
}

static void sht30_get_humi(sht30_info_t *sht30)
{
    unsigned short tmp;

    tmp                   = SHT3x_MeasureHM(sht30, 0x02);
    sht30->value.humidity = SHT3x_CalcRH(tmp);
}


static sht30_info_t *sht30_init(const char *bus_name, os_uint16_t addr)
{
    sht30_info_t *sht30 = NULL;

    sht30 = os_calloc(1, sizeof(sht30_info_t));
    if (sht30 == OS_NULL)
    {
        return NULL;
    }

    sht30->i2c.bus = os_i2c_bus_device_find(bus_name);
    if (sht30->i2c.bus == NULL)
    {

        os_free(sht30);
        return NULL;
    }

    sht30->i2c.client_addr = addr;
    sht30_reset(sht30);
    os_task_msleep(50);
    sht30_init_all(sht30);
    os_task_msleep(10);

    return sht30;
}

static os_size_t sht30_temp_fetch_data(struct os_sensor_device *sensor, void *buf, os_size_t len)
{
    sht30_info_t *sht30 = NULL;
    struct os_sensor_data *data  = NULL;

    OS_ASSERT(sensor);
    OS_ASSERT(sensor->info.type == OS_SENSOR_CLASS_TEMP);
    OS_ASSERT(buf);

    sht30 = (sht30_info_t *)sensor;
    data  = (struct os_sensor_data *)buf;

    sht30_get_temp(sht30);

    data->type      = sensor->info.type;
    data->data.temp = sht30->value.tempreture;
    data->timestamp = os_sensor_get_ts();

    return 0;
}

static os_err_t sht30_temp_control(struct os_sensor_device *sensor, int cmd, void *args)
{
    os_err_t      result = OS_EOK;
    sht30_info_t *sht30  = (sht30_info_t *)sensor;

    switch (cmd)
    {
    case OS_SENSOR_CTRL_GET_ID:
        *(uint8_t *)args = sht30->id;
        break;
    default:
        return OS_ERROR;
    }
    return result;
}

static struct os_sensor_ops sht30_temp_ops =
{
    sht30_temp_fetch_data,
    sht30_temp_control,
};

static int os_hw_sht30_temp_init(void)
{
    os_int8_t     result;
    sht30_info_t *sht30;

    sht30 = sht30_init(OS_SHT30_I2C_BUS_NAME, OS_SHT30_I2C_ADDR);
    if (sht30 == NULL)
    {
        goto __exit;
    }

    /* Temp */
    sht30->sensor.info.type       = OS_SENSOR_CLASS_TEMP;
    sht30->sensor.info.vendor     = OS_SENSOR_VENDOR_SENSIRION;
    sht30->sensor.info.model      = "sht30";
    sht30->sensor.info.unit       = OS_SENSOR_UNIT_MDCELSIUS;
    sht30->sensor.info.intf_type  = OS_SENSOR_INTF_I2C;
    sht30->sensor.info.range_max  = 100000;
    sht30->sensor.info.range_min  = 0;
    sht30->sensor.info.period_min = 300;
    sht30->sensor.ops             = &sht30_temp_ops;

    result = os_hw_sensor_register(&sht30->sensor, "sht30", OS_NULL);
    if (result != OS_EOK)
    {
        goto __exit;
    }

    return OS_EOK;

__exit:
    if (sht30)
        os_free(sht30);
    return OS_ERROR;
}

OS_DEVICE_INIT(os_hw_sht30_temp_init, OS_INIT_SUBLEVEL_LOW);

static os_size_t sht30_humi_fetch_data(struct os_sensor_device *sensor, void *buf, os_size_t len)
{
    sht30_info_t *sht30 = NULL;
    struct os_sensor_data *data  = NULL;

    OS_ASSERT(sensor);
    OS_ASSERT(sensor->info.type == OS_SENSOR_CLASS_HUMI);
    OS_ASSERT(buf);

    sht30 = (sht30_info_t *)sensor;
    data  = (struct os_sensor_data *)buf;

    sht30_get_humi(sht30);

    data->type      = sensor->info.type;
    data->data.humi = sht30->value.humidity;
    data->timestamp = os_sensor_get_ts();

    return 0;
}

static os_err_t sht30_humi_control(struct os_sensor_device *sensor, int cmd, void *args)
{
    os_err_t      result = OS_EOK;
    sht30_info_t *sht30  = (sht30_info_t *)sensor;

    switch (cmd)
    {
    case OS_SENSOR_CTRL_GET_ID:
        *(uint8_t *)args = sht30->id;
        break;
    default:
        return OS_ERROR;
    }
    return result;
}

static struct os_sensor_ops sht30_humi_ops =
{
    sht30_humi_fetch_data,
    sht30_humi_control,
};

static int os_hw_sht30_humi_init(void)
{
    os_int8_t     result;
    sht30_info_t *sht30;

    sht30 = sht30_init(OS_SHT30_I2C_BUS_NAME, OS_SHT30_I2C_ADDR);
    if (sht30 == NULL)
    {
        LOG_E(DBG_TAG,"sht30 humi init failed.");
        goto __exit;
    }

    /* humi */
    sht30->sensor.info.type       = OS_SENSOR_CLASS_HUMI;
    sht30->sensor.info.vendor     = OS_SENSOR_VENDOR_SENSIRION;
    sht30->sensor.info.model      = "sht30";
    sht30->sensor.info.unit       = OS_SENSOR_UNIT_MPERMILLAGE;
    sht30->sensor.info.intf_type  = OS_SENSOR_INTF_I2C;
    sht30->sensor.info.range_max  = 100000;
    sht30->sensor.info.range_min  = 0;
    sht30->sensor.info.period_min = 300;
    sht30->sensor.ops             = &sht30_humi_ops;

    result = os_hw_sensor_register(&sht30->sensor, "sht30", OS_NULL);
    if (result != OS_EOK)
    {
        LOG_E(DBG_TAG,"device humi register err code: %d", result);
        goto __exit;
    }

    LOG_D(DBG_TAG,"sht30 humi init success");
    return OS_EOK;

__exit:
    if (sht30)
        os_free(sht30);
    return OS_ERROR;
}
OS_DEVICE_INIT(os_hw_sht30_humi_init, OS_INIT_SUBLEVEL_LOW);
